package q1.project.core.jpa;

import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.Visitor;
import com.querydsl.core.types.dsl.DslExpression;

import jakarta.annotation.Nullable;
import org.springframework.util.ObjectUtils;

import java.io.Serial;
import java.util.Collection;
import java.util.Objects;
import java.util.function.BiFunction;

public final class DoBooleanBuilder implements Predicate, Cloneable {

    @Serial
    private static final long serialVersionUID = -4129485177345542519L;

    @Nullable
    private Predicate predicate;

    /**
     * Create an empty BooleanBuilder
     */
    public DoBooleanBuilder() {

    }

    /**
     * 使用给定的初始值创建DoBooleanBuilder
     * 形参:
     * 初始值
     */
    public DoBooleanBuilder(Predicate initial) {
        predicate = (Predicate) ExpressionUtils.extract(initial);
    }

    public static DoBooleanBuilder init() {
        return new DoBooleanBuilder();
    }

    @Override
    public <R, C> R accept(Visitor<R, C> v, C context) {
        if (predicate != null) {
            return predicate.accept(v, context);
        } else {
            return null;
        }
    }

    /**
     * 创建this和给定谓词的交集
     * 形参:
     * 右手侧和操作
     * 返回值:
     * 当前对象
     */
    public DoBooleanBuilder and(@Nullable Predicate right) {
        if (right != null) {
            if (predicate == null) {
                predicate = right;
            } else {
                predicate = ExpressionUtils.and(predicate, right);
            }
        }
        return this;
    }

    /**
     * 创建this与给定参数的并集的交集（this&&（arg1|| arg2…|| argN））
     * 形参:
     * args–谓词的并集
     * 返回值:
     */
    public DoBooleanBuilder andAnyOf(Predicate... args) {
        if (args.length > 0) {
            and(ExpressionUtils.anyOf(args));
        }
        return this;
    }

    /**
     * 创建this的插入和给定谓词的否定
     * 形参:
     * 右-谓词被否定
     * 返回值:
     * 当前对象
     */
    public DoBooleanBuilder andNot(Predicate right) {
        return and(right.not());
    }

    @Override
    public DoBooleanBuilder clone() throws CloneNotSupportedException {
        return (DoBooleanBuilder) super.clone();
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (o instanceof DoBooleanBuilder) {
            return Objects.equals(((DoBooleanBuilder) o).getValue(), predicate);
        } else {
            return false;
        }
    }

    @Nullable
    public Predicate getValue() {
        return predicate;
    }

    @Override
    public int hashCode() {
        return predicate != null ? predicate.hashCode() : 0;
    }

    /**
     * 如果设置了值，则返回true，否则返回false
     * 返回值:
     * 如果初始化，则为true，否则为false
     */
    public boolean hasValue() {
        return predicate != null;
    }

    @Override
    public DoBooleanBuilder not() {
        if (predicate != null) {
            predicate = predicate.not();
        }
        return this;
    }

    /**
     * Create the union of this and the given predicate
     *
     * @param right right hand side of {@code or} operation
     * @return the current object
     */
    public DoBooleanBuilder or(@Nullable Predicate right) {
        if (right != null) {
            if (predicate == null) {
                predicate = right;
            } else {
                predicate = ExpressionUtils.or(predicate, right);
            }
        }
        return this;
    }

    public <T, V extends DslExpression<?>> DoBooleanBuilder or(V path, T value, BiFunction<V, T, Predicate> function) {
        if (!ObjectUtils.isEmpty(value)) {
            or(function.apply(path, value));
        }
        return this;
    }

    /**
     * 创建this和给定参数的交集的并集（this ||（arg1&&arg2…&&argN））
     * 形参:
     * args–谓词的交集
     * 返回值:
     * 当前对象
     */
    public DoBooleanBuilder orAllOf(Predicate... args) {
        if (args.length > 0) {
            or(ExpressionUtils.allOf(args));
        }
        return this;
    }

    /**
     * 创建this和给定谓词否定的并集
     * 形参:
     * 右-谓词被否定
     * 返回值:
     * 当前对象
     */
    public DoBooleanBuilder orNot(Predicate right) {
        return or(right.not());
    }

    @Override
    public Class<? extends Boolean> getType() {
        return Boolean.class;
    }

    public <T, V extends DslExpression<?>> DoBooleanBuilder and(V path, T value, BiFunction<V, T, Predicate> function) {
        if (!ObjectUtils.isEmpty(value)) {
            and(function.apply(path, value));
        }
        return this;
    }

    public <T, V extends DslExpression<?>> DoBooleanBuilder and(V path, Collection<T> values, BiFunction<V, Collection<T>, Predicate> function) {
        if (!ObjectUtils.isEmpty(values)) {
            and(function.apply(path, values));
        }
        return this;
    }

    @Override
    public String toString() {
        return predicate != null ? predicate.toString() : super.toString();
    }


}
